﻿/* Copyright (C) 2007-2010 STS Soft

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace STSdb.General.Buffers
{
    public static class ArrayExtensions
    {
        /// <summary>
        /// compares two arrays as a big numbers (from left to right)
        /// </summary>
        public unsafe static int CompareTo(this byte[] self, byte[] buffer, int length)
        {
            if (self == null)
                throw new ArgumentNullException("self");
            if (buffer == null)
                throw new ArgumentNullException("buffer");

            int count = length / sizeof(ulong);
            fixed (byte* raw1 = self, raw2 = buffer)
            {
                ulong* array1 = (ulong*)raw1;
                ulong* array2 = (ulong*)raw2;
                for (int i = 0; i < count; i++)
                {
                    int cmp = array1[i].CompareTo(array2[i]);
                    if (cmp != 0)
                    {
                        for (int j = sizeof(ulong) * i; /*j < sizeof(ulong) * (i + 1) */; j++)
                        {
                            cmp = raw1[j].CompareTo(raw2[j]);
                            if (cmp != 0)
                                return cmp;
                        }
                    }
                }
            }

            int remainder = length % sizeof(ulong);
            for (int i = length - remainder; i < length; i++)
            {
                int cmp = self[i].CompareTo(buffer[i]);
                if (cmp != 0)
                    return cmp;
            }

            return 0;
        }

        public static int CompareTo(this byte[] self, byte[] buffer)
        {
            int length = Math.Min(self.Length, buffer.Length);

            int cmp = CompareTo(self, buffer, length);
            if (cmp != 0)
                return cmp;

            return self.Length.CompareTo(buffer.Length);
        }

        /// <summary>
        /// compares two arrays as a big numbers (from right to left)
        /// </summary>
        public unsafe static int ReverseCompareTo(this byte[] buffer1, byte[] buffer2)
        {
            int length = buffer2.Length;
            int count = length / sizeof(ulong);
            int remainder = length % sizeof(ulong);

            fixed (byte* raw1 = buffer1, raw2 = buffer2)
            {
                ulong* array1 = (ulong*)(raw1 + remainder);
                ulong* array2 = (ulong*)(raw2 + remainder);

                for (int i = count - 1; i >= 0; i--)
                {
                    int cmp = array1[i].CompareTo(array2[i]);
                    if (cmp != 0)
                        return cmp;
                }
            }

            for (int i = remainder - 1; i >= 0; i--)
            {
                int cmp = buffer1[i].CompareTo(buffer2[i]);
                if (cmp != 0)
                    return cmp;
            }

            return 0;
        }

        public static bool EqualsТо(this byte[] self, byte[] buffer, int length)
        {
            return CompareTo(self, buffer, length) == 0;
        }

        public static bool EqualsТо(this byte[] self, byte[] buffer)
        {
            if (self.Length != buffer.Length)
                return false;

            return EqualsТо(self, buffer, self.Length);
        }

        public static unsafe int GetHashCode(this byte[] self)
        {
            const int CONSTANT = 17;

            int hashCode = 37;
            int count = self.Length / sizeof(int);
            fixed (byte* buffer = self)
            {
                int* ibuffer = (int*)buffer;
                for (int i = 0; i < count; i++)
                    hashCode = hashCode * CONSTANT + ibuffer[i];
            }

            int rest = self.Length % sizeof(int);
            for (int i = self.Length - rest; i < self.Length; i++)
                hashCode = hashCode * CONSTANT + self[i];

            return hashCode;
        }

        public static byte[] Middle(this byte[] self, int startIndex, int count)
        {
            byte[] buffer = new byte[count];
            Buffer.BlockCopy(self, startIndex, buffer, 0, count);
            return buffer;
        }

        public static byte[] Left(this byte[] self, int count)
        {
            return self.Middle(0, count);
        }

        public static byte[] Right(this byte[] self, int count)
        {
            return self.Middle(self.Length - count, count);
        }

        /// <returns>>Returns true on Overflow.</returns>
        public static bool Increment(this byte[] self)
        {
            for (int i = 0; i < self.Length; i++)
            {
                self[i]++;
                if (self[i] != Byte.MinValue)
                    return false;
            }

            return true;
        }

        /// <returns>Returns true on Overflow.</returns>
        public static bool Decrement(this byte[] self)
        {
            for (int i = 0; i < self.Length; i++)
            {
                self[i]--;
                if (self[i] != Byte.MaxValue)
                    return false;
            }

            return true;
        }
    }
}
